package net.disk;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.ArrayList;

import net.fusejna.DirectoryFiller;
import net.fusejna.StructStat.StatWrapper;
import net.fusejna.types.TypeMode.NodeType;

class Netdirectory extends Netpath
{
	ArrayList<Netpath> contents = new ArrayList<Netpath>();

	public Netdirectory(final String name)
	{
		super(name);
	}

	public Netdirectory(final String name, final Netdirectory parent)
	{
		super(name, parent);
	}

	public void add(final Netpath path)
	{
		contents.add(path);
		path.parent = this;
	}

	@Override
	protected Netpath find(String path)
	{
		if (super.find(path) != null) {
			return super.find(path);
		}
		while (path.startsWith("/")) {
			path = path.substring(1);
		}
		if (!path.contains("/")) {
			for (final Netpath p : contents) {
				if (p.name.equals(path)) {
					return p;
				}
			}
			return null;
		}
		final String nextName = path.substring(0, path.indexOf("/"));
		final String rest = path.substring(path.indexOf("/"));
		for (final Netpath p : contents) {
			if (p.name.equals(nextName)) {
				return p.find(rest);
			}
		}
		return null;
	}

	@Override
	protected void getattr(final StatWrapper stat)
	{
		stat.setMode(NodeType.DIRECTORY);
	}

	public void mkdir(final String dirname)
	{
		contents.add(new Netdirectory(dirname, this));
	}

	public void mkfile(final String filename)
	{
		contents.add(new Netfile(filename, this));
	}

	public void read(final DirectoryFiller filler)
	{
		for (final Netpath path : contents) {
			filler.add(path.name);
		}
	}
}

class Netfile extends Netpath
{
	private ByteBuffer contents = ByteBuffer.allocate(0);
	String content;

	public Netfile(final String name)
	{
		super(name);
	}

	public Netfile(final String name, final Netdirectory parent)
	{
		super(name, parent);
	}

	public Netfile(final String name, final String text)
	{
		super(name);
		content = text;
		try {
			final byte[] contentBytes = text.getBytes("UTF-8");
			contents = ByteBuffer.wrap(contentBytes);
		}
		catch (final UnsupportedEncodingException e) {
			// Not going to happen
		}
	}

	@Override
	protected void getattr(final StatWrapper stat)
	{
		stat.setMode(NodeType.FILE);
		stat.size(contents.capacity());
	}

	public int read(final ByteBuffer buffer, final long size, final long offset)
	{
		final int bytesToRead = (int) Math.min(contents.capacity() - offset, size);
		final byte[] bytesRead = new byte[bytesToRead];
		contents.position((int) offset);
		contents.get(bytesRead, 0, bytesToRead);
		buffer.put(bytesRead);
		contents.position(0); // Rewind
		return bytesToRead;
	}

	public int write(final ByteBuffer buffer, final long size, final long offset)
	{
		final int maxWriteIndex = (int) (offset + size);
		if (maxWriteIndex > contents.capacity()) {
			// Need to create a new, larger buffer
			final ByteBuffer newContents = ByteBuffer.allocate(maxWriteIndex);
			newContents.put(contents);
			contents = newContents;
		}
		final byte[] bytesToWrite = new byte[(int) size];
		buffer.get(bytesToWrite, 0, (int) size);
		contents.position((int) offset);
		contents.put(bytesToWrite);
		contents.position(0); // Rewind
		return (int) size;
	}
}

public abstract class Netpath
{
	protected String name;
	protected Netdirectory parent;
	protected String fileid;
	protected String from;

	public Netpath(final String name)
	{
		this.name = name;
	}

	public Netpath(final String name, final Netdirectory parent)
	{
		this.name = name;
		this.parent = parent;
	}

	public void delete()
	{
		if (parent != null) {
			parent.contents.remove(this);
			parent = null;
		}
	}

	protected Netpath find(String path)
	{
		while (path.startsWith("/")) {
			path = path.substring(1);
		}
		if (path.equals(name) || path.isEmpty()) {
			return this;
		}
		return null;
	}

	protected abstract void getattr(StatWrapper stat);

	public String getFrom()
	{
		return from;
	}

	public String getID()
	{
		return fileid;
	}

	void rename(String newName)
	{
		while (newName.startsWith("/")) {
			newName = newName.substring(1);
		}
		name = newName;
	}

	public void setFrom(final String fr)
	{
		from = fr;
	}

	public void setID(final String id)
	{
		fileid = id;
	}
}
